﻿using SharpDX;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace MikadoShooter
{
    public class DrawShooter : DrawBase
    {
        private const int Lines = 1000;
        private const int HoldShootTime = 500;
        private readonly float xRes;
        private readonly float yRes;
        private readonly StatusBar statusBar;
        private readonly MouseHandler mouseHandler;
        private readonly Stopwatch stopWatch;
        private readonly TargetAction targetAction;
        private readonly Action<Vector4, Vector4> updateColor;
        private readonly Level level;
        private readonly SoundPlayer soundPlayer;
        private eShootMode shootMode;
        private Collision collision;
        private Vector4Shoot Shoot { get{ return (Vector4Shoot)Vector4Base; } }

        public DrawShooter(DXWindow dXWindow, TargetAction targetAction, Action<Vector4, Vector4> updateColor, Level level,SoundPlayer soundPlayer)
            : base(CalcVerticals(), 
                new Vector4Shoot(CalcVerticals(), 
                new Vector4(0.0f, -1.01f, 0.0f, 1.0f), 
                RandomExt.GetRandomColor(level.Colors), 
                RandomExt.GetRandomColor(level.Colors)))
        {
            this.targetAction = targetAction;
            this.updateColor = updateColor;
            this.level = level;
            this.soundPlayer = soundPlayer; 

            statusBar = new StatusBar();
            stopWatch = new Stopwatch();
            
            shootMode = eShootMode.End;

            updateColor(Shoot.CurrentColor, Shoot.NextColor);
            mouseHandler = dXWindow.MouseHandler;

            //Float resolution  (-1.0f to 1.0f)
            xRes = 2.0f / (dXWindow.RenderForm.Width - 5);
            yRes = 2.0f / (dXWindow.RenderForm.Height - statusBar.Size.Height - 5);
        }

        public void Update(Point location)
        {
            if (collision != null && collision.IsAHit && collision.LineIndex != null)
                Shoot.ItsAHit();

            //Hold the shoot after impact until stopWatch elapsed time...
            if (shootMode != eShootMode.End && stopWatch.IsRunning)
            {
                if (stopWatch.ElapsedMilliseconds <= HoldShootTime)
                    return; //Hold it

                //Reset values for new shoots
                switch (shootMode)
                {
                    case eShootMode.Bounch:
                        Shoot.Reset(Verticals, null);
                        break;
                    default:
                        Shoot.Reset(Verticals, RandomExt.GetRandomColor(level.Colors));
                        updateColor(Shoot.CurrentColor, Shoot.NextColor);
                        break;
                }

                stopWatch.Reset();
            }

            //New shoot
            if (mouseHandler.MouseLeft && shootMode == eShootMode.End)
            {
                //MousePointer location
                Vector4 mouse = new Vector4(
                    xRes*(Cursor.Position.X - location.X - 5) - 1.0f,
                    1.0f - yRes*(Cursor.Position.Y - location.Y - statusBar.Size.Height - 5),
                    0.0f, 0.0f);

                //Check switch colors!
                if (mouse.Y < -0.8f)
                {
                    soundPlayer.PlaySound(eSoundname.Color);

                    //Switch colors
                    Shoot.SwitchColor();

                    //Update background
                    updateColor(Shoot.CurrentColor, Shoot.NextColor);
                    return;
                }

                shootMode = eShootMode.New;
                Shoot.Shoot(mouse);
            }

            if (shootMode == eShootMode.Hit)
                shootMode = eShootMode.End;


            //Update the shoot if needed
            if (shootMode != eShootMode.End)
                ShootUpdate();
        }

        private void ShootUpdate()
        {
            Vector2? check;
            Vector4 endPos = Shoot.StartPos + Vector4.Multiply(Shoot.Direction, Verticals * 0.01f);
            
            if ((collision = CollisionCheckHelper.IsCollision(targetAction.GetTargets(), Shoot.StartPos, endPos, Shoot.CurrentColor)).IsAHit)
            {
                shootMode = eShootMode.Hit;
                if (collision.LineIndex != null)
                {
                    soundPlayer.PlaySound(eSoundname.Hit);
                    Debug.WriteLine("Hit - Right color");

                    if (!targetAction.RemoveTarget(collision.LineIndex))      //Remove line
                        return;
                }
                else
                {
                    soundPlayer.PlaySound(eSoundname.Miss);
                    Debug.WriteLine("Hit - Wrong color");

                    collision.Closer();                                 //Gui beauty fix
                    if (!targetAction.SetMissTarget())                  //Move lines closer
                        return;
                }

                UpdateData(collision.Hit);
            }
            else if ((check = CollisionUpdate(eWall.Up, endPos)) != null)
            {
                soundPlayer.PlaySound(eSoundname.Wall);
                HitUpdate(check.Value, "Up", new Vector2(1, 1), true);
            }
            else if ((check = CollisionUpdate(eWall.Right, endPos)) != null)
            {
                soundPlayer.PlaySound(eSoundname.Wall);
                HitUpdate(check.Value, "Right", new Vector2(-1, 1));
            }
            else if ((check = CollisionUpdate(eWall.Left, endPos)) != null)
            {
                soundPlayer.PlaySound(eSoundname.Wall);
                HitUpdate(check.Value, "Left", new Vector2(1, 1));
            }
            else if ((check = CollisionUpdate(eWall.DownRight, endPos)) != null && shootMode == eShootMode.Bounch)
            {
                soundPlayer.PlaySound(eSoundname.Miss);
                HitUpdate(check.Value, "DownRight");
            }
            else if ((check = CollisionUpdate(eWall.DownLeft, endPos)) != null && shootMode == eShootMode.Bounch)
            {
                soundPlayer.PlaySound(eSoundname.Miss);
                HitUpdate(check.Value, "DownLeft");
            }
            else if ((check = CollisionUpdate(eWall.Down, endPos)) != null && shootMode == eShootMode.Bounch)
            {
                soundPlayer.PlaySound(eSoundname.Miss);
                HitUpdate(check.Value, "Down");
            }
            else
                throw new ApplicationException("HitUpdate - BUG!");

            stopWatch.Start();
        }

        private Vector2? CollisionUpdate(eWall wall, Vector4 endPos)
        {
            return CollisionCheckHelper.IsCollision(Background.GetLine(wall).Start, Background.GetLine(wall).End, Shoot.StartPos, endPos);
        }

        private void HitUpdate(Vector2 pos, string debug, Vector2? side = null, bool isUp = false)
        {
            Debug.WriteLine("Hit - " + debug);
            UpdateData(pos);

            if (side == null)   //No bounch
            {
                shootMode = eShootMode.Hit;
            }
            else if (isUp)      //Bounch Up
            {
                shootMode = eShootMode.Bounch;
                Shoot.BounchUp(pos, side.Value);
            }
            else                //Bounch Wall
            {
                shootMode = eShootMode.Bounch;
                Shoot.BounchWall(pos, side.Value);
            }
        }

        private void UpdateData(Vector2 endPos)
        {
            int shooterIndex = 0;

            for (int i = 0; i < Verticals; i++)
            {
                Data[shooterIndex++] = Vector4.SmoothStep(Shoot.StartPos, new Vector4(endPos, 0.0f, 1.0f), i * 0.01f);
                Data[shooterIndex++] = Shoot.CurrentColor;
            }

            if (shooterIndex != CalcVerticals() * 2)
                throw new ApplicationException("Error CalcVerticals in drawShooter..");
        }

        private static int CalcVerticals()
        {
            return Lines;
        }

        public Vector4 GetCurrentColor()
        {
            return Shoot.CurrentColor;
        }
    }
}